Skip to content

[ECS-Plugin] Implement ECS_ SYNC stage#6559

Merged
khanhtc1202 merged 10 commits intopipe-cd:masterfrom
armistcxy:ecs-plugin/sync
Mar 11, 2026
Merged

[ECS-Plugin] Implement ECS_ SYNC stage#6559
khanhtc1202 merged 10 commits intopipe-cd:masterfrom
armistcxy:ecs-plugin/sync

Conversation

@armistcxy
Copy link
Contributor

What this PR does:
Implementing ECS_SYNC stage. The quick sync and sync option will use this stage by default

Why we need it:

Which issue(s) this PR fixes: Part of #6443

Fixes #

Does this PR introduce a user-facing change?:

  • How are users affected by this change:
  • Is this breaking change:
  • How to migrate (if breaking change):

Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
when recreate is enable, stop the current running tasks (i.e. set
desired #tasks of service to 0), then rollout new taskset and set the
number of desired #tasks back to original value

Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
@armistcxy armistcxy requested a review from a team as a code owner March 6, 2026 08:31
@armistcxy armistcxy self-assigned this Mar 6, 2026
@armistcxy
Copy link
Contributor Author

armistcxy commented Mar 6, 2026

There some works still needed to do before merging, I 've just created this PR so you (@khanhtc1202) can have a quick glance and give early feedbacks ^ ^

To make it easy when reviewing PR, you can refer to these resources:

Some changes have been made (different from v0)

  1. No longer check PipCD managed tags for service: Change
  2. Check service's health status before update service: Check
  3. Remove redundant slice copy in client.ListTags
func (c *client) ListTags(ctx context.Context, resourceArn string) ([]types.Tag, error) {
	input := &ecs.ListTagsForResourceInput{
		ResourceArn: aws.String(resourceArn),
	}

	output, err := c.ecsClient.ListTagsForResource(ctx, input)
	if err != nil {
		return nil, err
	}

	tags := make([]types.Tag, 0, len(output.Tags))
	for _, t := range output.Tags {
		tags = append(tags, types.Tag{
			Key:   t.Key,
			Value: t.Value,
		})
	}
	return tags, nil
}

TODO (in works):

  • Figure out how to implement test for sync stage -> use mock for testing (logic-focus only, not real integrated) add unit test for sync function
  • Figure out how to support other properties/fields for tasks and services (currently only supports v0-compatible props)

@armistcxy
Copy link
Contributor Author

armistcxy commented Mar 6, 2026

About the testing part, I've already made something like this to allow real integration test. But this is not safe due to potential waste if not handle cleaning up resources properly after test, maybe mocking will be better here

	var (
		clusterARN   = os.Getenv("ECS_CLUSTER_ARN")
		subnetIDsStr = os.Getenv("SUBNET_IDS")
		sgIDsStr     = os.Getenv("SECURITY_GROUP_IDS")

		subnetIDs []string
		sgIDs     []string
	)

	if clusterARN == "" {
		t.Skip("ECS_CLUSTER_ARN is not set, skipping ECS sync stage test")
	}
	if subnetIDsStr == "" {
		t.Skip("SUBNET_IDS is not set, skipping ECS sync stage test")
	}
	for _, id := range strings.Split(subnetIDsStr, ",") {
		subnetIDs = append(subnetIDs, strings.TrimSpace(id))
	}
	if len(subnetIDs) == 0 {
		t.Skip("SUBNET_IDS is empty, skipping ECS sync stage test")
	}
	if sgIDsStr == "" {
		t.Skip("SECURITY_GROUP_IDS is not set, skipping ECS sync stage test")
	}

@khanhtc1202
Copy link
Member

For DetermineVersion function, I think you can ignore it in this PR, we can implement it by a separated PR

@khanhtc1202
Copy link
Member

Also, your commits had shown as Unverified currently, could you check this docs and update Github setting to fix that
https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
Probably, this is caused by Github SSH Key is not being used

Comment on lines +71 to +79
// Add PipeCD-managed tags
serviceDef.Tags = append(serviceDef.Tags,
provider.MakeTags(map[string]string{
provider.LabelManagedBy: provider.ManagedByECSPlugin,
provider.LabelPiped: input.Request.Deployment.PipedID,
provider.LabelApplication: input.Request.Deployment.ApplicationID,
provider.LabelCommitHash: input.Request.TargetDeploymentSource.CommitHash,
})...,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about to move in inside LoadServiceDefinition function? Since we may need to do the same when implement rollout stage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think LoadServiecDefinition purpose is parsing manifest into types.Service, adding tags here I just feel not the right place to do that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, adding tags explicitly in execute stage function will avoid confusion for the maintain work in the future

Also, LoadServiceDefinition current input consists of 2 params: appDir, serviceDefinition string, if moving adding tags to this function, we need to add pipeId, applicationId, commitHash, which will make the function bloat and drift away from its function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let re-check the service tags value of service object fetched from API when we update service. If it contains the tags as well, we can safely move this tag assign logic to load service definition, ensure service object in data flow will always have the tags. 👍
For now, let's keep the implementation as your 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service returned from UpdateService API doesn't contain tags but all the stage use applyServiceDefinition and inside that function the tags are re assigned

// Re-assign tags to service object because UpdateService API doesn't return tags.
service.Tags = serviceDefinition.Tags

And my bad, the v0 loadServiceDefinition already add the tags inside

func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds *deploysource.DeploySource) (types.Service, bool) {
	in.LogPersister.Infof("Loading service manifest at commit %s", ds.Revision)

	serviceDefinition, err := provider.LoadServiceDefinition(ds.AppDir, serviceDefinitionFile)
	if err != nil {
		in.LogPersister.Errorf("Failed to load ECS service definition (%v)", err)
		return types.Service{}, false
	}

	serviceDefinition.Tags = append(
		serviceDefinition.Tags,
		provider.MakeTags(map[string]string{
			provider.LabelManagedBy:   provider.ManagedByPiped,
			provider.LabelPiped:       in.PipedConfig.PipedID,
			provider.LabelApplication: in.Deployment.ApplicationId,
			provider.LabelCommitHash:  in.Deployment.CommitHash(),
		})...,
	)

	in.LogPersister.Infof("Successfully loaded the ECS service definition at commit %s", ds.Revision)
	return serviceDefinition, true
}

Update:

// LoadServiceDefinition returns Service object from a given service definition file.
func LoadServiceDefinition(appDir, serviceDefinition string, input *sdk.ExecuteStageInput[config.ECSApplicationSpec]) (types.Service, error) {
	path := filepath.Join(appDir, serviceDefinition)
	service, err := loadServiceDefinition(path)
	if err != nil {
		return types.Service{}, err
	}
	// Add PipeCD-managed tags
	service.Tags = append(service.Tags,
		MakeTags(map[string]string{
			LabelManagedBy:   ManagedByECSPlugin,
			LabelPiped:       input.Request.Deployment.PipedID,
			LabelApplication: input.Request.Deployment.ApplicationID,
			LabelCommitHash:  input.Request.TargetDeploymentSource.CommitHash,
		})...,
	)
	return service, nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Signed-off-by: Hoang Ngo <adlehoang118@gmail.com>
Copy link
Member

@khanhtc1202 khanhtc1202 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 💯

@khanhtc1202 khanhtc1202 merged commit 99fede9 into pipe-cd:master Mar 11, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants